﻿#include "mediaplayer.h"

#include <QThread>
#include <QDebug>
#include <QFile>
#include <QIODevice>
#include <QDateTime>
#include <QTime>
#include <QUdpSocket>
#include <QAudioInput>
#include <QAudioSource>
#include <QMediaDevices>
#include <UdpMsg.h>
extern "C"{
#include "libswresample/swresample.h"
#include "libavformat/avformat.h"
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
}
#include "../ConfigDef.h"
MediaPlayer::MediaPlayer(QString taskId, int sessionId, QString device, int bitrate, CH_MODE chMode, bool useFFmpeg, QObject *parent)
    :QObject(parent),_taskId(taskId), _sessionId(sessionId),
    _device(device),_bitrate(bitrate),
    _chMode(chMode),_isRunning(false),
    _isPaused(false)
{
    _sendInc = 0;
    _frameIndex = 0;

    QSettings settings(CONFIG_FILE,QSettings::IniFormat);
    _audioAddress = QHostAddress(settings.value("AudioAddress").toString());
    _audioPort = settings.value("AudioPort").toInt();
    _saveRecord = settings.value("SaveLocalRecord").toBool();

    _workThread = new QThread(this);
    this->moveToThread(_workThread);

    connect(_workThread,&QThread::started,this,&MediaPlayer::recordAudio);

    connect(this,&MediaPlayer::sigRecordFinished,_workThread,&QThread::quit);
    connect(_workThread,&QThread::finished,this,&MediaPlayer::sigFinished);
}

MediaPlayer::~MediaPlayer()
{
    qDebug()<<"MediaPlayer delete";
}

void MediaPlayer::recordAudioFFmepg()
{
    qDebug()<<"session:"<<_sessionId<<QThread::currentThreadId();
    AVFormatContext *input_ctx = nullptr;
    AVDictionary *options = nullptr;
    AVCodecContext *dec_ctx = nullptr;
    AVCodecContext *enc_ctx = nullptr;
    AVFilterGraph *filter_graph = nullptr;
    SwrContext *swr_ctx = swr_alloc();
    AVFrame *dec_frame = av_frame_alloc();
    AVFrame *filtered_frame = av_frame_alloc();
    AVFrame* resampled_frame = av_frame_alloc();
    AVFrame *enc_frame = av_frame_alloc();
    AVPacket *input_pkt = av_packet_alloc();
    AVPacket *enc_pkt = av_packet_alloc();
    uint8_t **resampled_buffer = nullptr;

    FILE *filemp3 = nullptr;
    if(_saveRecord){
        fopen_s(&filemp3,QString("record/record_%1_%2.mp3").arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"),_taskId).toStdString().c_str(),"wb");
    }
    QUdpSocket udpSocket;
    try {
        if(_audioAddress.isNull() || _audioPort>65535 || _audioPort<=0){
            throw std::runtime_error("Invalid AudioAddress or AudioPort");
        }
        // 打开音频输入设备
        const AVInputFormat *input_fmt = av_find_input_format("dshow");
        av_dict_set(&options, "sample_rate", "44100", 0); // 设置采样率
        av_dict_set(&options, "channels", "2", 0);        // 设置为双声道
        // 设置音频缓冲区大小（直接影响音频帧的大小）
        av_dict_set(&options, "audio_buffer_size", "27", 0);
        auto device = QString("audio=%1").arg(_device);
        auto ret = avformat_open_input(&input_ctx, device.toStdString().c_str(), input_fmt, &options);
        if (ret < 0) {
            throw std::runtime_error(QString("Faild to open input : %1").arg(avErr2Str(ret)).toStdString());
        }

        // 查找音频流信息
        ret = avformat_find_stream_info(input_ctx, nullptr);
        if ( ret < 0) {
            throw std::runtime_error(QString("Failed to find stream info : %1").arg(avErr2Str(ret)).toStdString());
        }
        int audio_stream_idx = av_find_best_stream(input_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
        if(audio_stream_idx < 0){
            throw std::runtime_error(QString("No audio stream found : %1").toStdString());
        }

        // 初始化解码器
        const AVCodec *decoder = avcodec_find_decoder(input_ctx->streams[audio_stream_idx]->codecpar->codec_id);
        dec_ctx = avcodec_alloc_context3(decoder);
        avcodec_parameters_to_context(dec_ctx, input_ctx->streams[audio_stream_idx]->codecpar);

        ret = avcodec_open2(dec_ctx, decoder, nullptr);
        if ( ret< 0) {
            throw std::runtime_error(QString("Failed to open decoder : %1").arg(avErr2Str(ret)).toStdString());
        }

        // 初始化 MP3 编码器
        const AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_MP3);
        enc_ctx = avcodec_alloc_context3(encoder);
        enc_ctx->bit_rate = _bitrate * 1000;                              // 设置比特率
        enc_ctx->sample_rate = dec_ctx->sample_rate;             // 采样率
        AVChannelLayout ch_layout;
        av_channel_layout_default(&ch_layout,2);    // 双声道
        av_channel_layout_copy(&enc_ctx->ch_layout,&ch_layout);

        enc_ctx->sample_fmt = AV_SAMPLE_FMT_S16P;           // 设置样本格式
        enc_ctx->time_base = {1, enc_ctx->sample_rate};          // 时间基
        ret = avcodec_open2(enc_ctx, encoder, nullptr);
        if ( ret < 0) {
            throw std::runtime_error(QString("Failed to open encoder : %1").arg(avErr2Str(ret)).toStdString());
        }

        // 配置过滤器链

        AVFilterContext *buffersrc_ctx=nullptr,*buffersink_ctx = nullptr,*pan_ctx = nullptr;

        filter_graph = avfilter_graph_alloc();

        if (dec_ctx->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC)
            av_channel_layout_default(&dec_ctx->ch_layout, dec_ctx->ch_layout.nb_channels);
        char args[512];
        snprintf(args, sizeof(args),
                 "time_base=1/%d:sample_rate=%d:sample_fmt=%s:channel_layout=stereo",
                 dec_ctx->sample_rate, dec_ctx->sample_rate, av_get_sample_fmt_name(dec_ctx->sample_fmt));


        ret = avfilter_graph_create_filter(&buffersrc_ctx, avfilter_get_by_name("abuffer"), "in", args, nullptr, filter_graph);
        if(ret < 0){
            throw std::runtime_error(QString("Failed to create src avfilter context : %1").arg(avErr2Str(ret)).toStdString());
        }
        ret = avfilter_graph_create_filter(&buffersink_ctx, avfilter_get_by_name("abuffersink"), "out", nullptr, nullptr, filter_graph);
        if(ret < 0){
            throw std::runtime_error(QString("Failed to create sink avfilter context : %1").arg(avErr2Str(ret)).toStdString());
        }

        const char *pan_args = "stereo|c0=0.5c0+0.5*c1|c1=0.5*c0+0.5*c1"; // 默认双声道
        if (_chMode == CH_LEFT) {
            pan_args = "stereo|c0=c0|c1=c0"; // 左声道
        } else if (_chMode == CH_RIGHT) {
            pan_args = "stereo|c0=c1|c1=c1"; // 右声道
        }

        ret = avfilter_graph_create_filter(&pan_ctx, avfilter_get_by_name("pan"), "pan", pan_args, nullptr, filter_graph);
        if(ret < 0){
            throw std::runtime_error(QString("Failed to create pan avfilter context : %1").arg(avErr2Str(ret)).toStdString());
        }

        ret = avfilter_link(buffersrc_ctx, 0, pan_ctx, 0);
        if(ret < 0){
            throw std::runtime_error(QString("Failed to link src avfilter context to pan avfilter context: %1").arg(avErr2Str(ret)).toStdString());
        }
        ret = avfilter_link(pan_ctx, 0, buffersink_ctx, 0);
        if(ret < 0){
            throw std::runtime_error(QString("Failed to link pan avfilter context to sink avfilter context: %1").arg(avErr2Str(ret)).toStdString());
        }

        ret = avfilter_graph_config(filter_graph, nullptr);
        if(ret < 0){
            throw std::runtime_error(QString("Failed to config avfilter graph: %1").arg(avErr2Str(ret)).toStdString());
        }

        // 设置 SwrContext 参数，目标格式为 s16p
        ret = swr_alloc_set_opts2(&swr_ctx,
                                  &enc_ctx->ch_layout, enc_ctx->sample_fmt,enc_ctx->sample_rate,
                                  &dec_ctx->ch_layout, dec_ctx->sample_fmt, dec_ctx->sample_rate,
                                  0, nullptr);
        if ( ret < 0) {
            throw std::runtime_error(QString("Failed to set swr context options : %1").arg(avErr2Str(ret)).toStdString());
        }
        ret = swr_init(swr_ctx);
        if( ret < 0){
            throw std::runtime_error(QString("Failed to init swr context : %1").arg(avErr2Str(ret)).toStdString());
        }

        //一帧mp3所对应的filter_frame数据
        int frame_bytes_per_ch = enc_ctx->frame_size * av_get_bytes_per_sample(enc_ctx->sample_fmt);
        int max_buffer_frame = 30;
        //filtered_buffer中的数据长度
        int buffer_size = 0;
        int max_buffer_size =  av_samples_alloc_array_and_samples(&resampled_buffer, nullptr, enc_ctx->ch_layout.nb_channels,
                                                                 enc_ctx->frame_size * max_buffer_frame, enc_ctx->sample_fmt, 0) / enc_ctx->ch_layout.nb_channels;
        //配置resampled_frame
        resampled_frame->sample_rate = enc_ctx->sample_rate;
        resampled_frame->format = enc_ctx->sample_fmt;
        av_channel_layout_copy(&resampled_frame->ch_layout,&enc_ctx->ch_layout);
        //配置enc_frame
        enc_frame->sample_rate = enc_ctx->sample_rate;
        enc_frame->format = enc_ctx->sample_fmt;
        av_channel_layout_copy(&enc_frame->ch_layout,&enc_ctx->ch_layout);
        enc_frame->nb_samples = enc_ctx->frame_size;

        // 采集音频并发送
        emit playerStateChanged(PLAYER_START,_taskId,_sessionId);
        while (av_read_frame(input_ctx, input_pkt) >= 0) {
            QMutexLocker locker(&_mutex);
            if(!_isRunning){
                break;
            }
            if(_isPaused){
                av_packet_unref(input_pkt);
                continue;
            }
            locker.unlock();
            if (input_pkt->stream_index == audio_stream_idx) {
                //传入解码器
                // qDebug()<<"1"<<QTime::currentTime();
                if (avcodec_send_packet(dec_ctx, input_pkt) == 0) {
                    //获取解码器数据
                    while (avcodec_receive_frame(dec_ctx, dec_frame) == 0) {
                        // qDebug()<<"2"<<QTime::currentTime();
                        qDebug()<<"123123"<<dec_frame->nb_samples;

                        // 传入过滤器
                        if (av_buffersrc_add_frame(buffersrc_ctx, dec_frame) == 0) {
                            //获取过滤器数据
                            while (av_buffersink_get_frame(buffersink_ctx, filtered_frame) >= 0) {
                                // qDebug()<<"3"<<QTime::currentTime();

                                ret = swr_convert_frame(swr_ctx,resampled_frame,filtered_frame);
                                if(ret<0){
                                    qDebug()<<"convert err:"<<avErr2Str(ret);
                                    continue;
                                }
                                // qDebug()<<"4"<<QTime::currentTime();

                                //单个声道数据总量
                                int bytes_per_ch = resampled_frame->nb_samples *
                                                   av_get_bytes_per_sample((enum AVSampleFormat)resampled_frame->format);

                                // 如果不超过max_buffer_size，讲数据拷贝到缓存区
                                if(buffer_size + bytes_per_ch <= max_buffer_size){
                                    for(int ch =0;ch<resampled_frame->ch_layout.nb_channels;ch++){
                                        memcpy(resampled_buffer[ch]+buffer_size,
                                               resampled_frame->data[ch],bytes_per_ch);
                                    }

                                    buffer_size += bytes_per_ch;
                                }
                                // qDebug()<<"5"<<QTime::currentTime();

                                // 如果缓存数据超过一帧
                                while (buffer_size >= frame_bytes_per_ch) {
                                    for(int ch = 0;ch<enc_frame->ch_layout.nb_channels;ch++){
                                        enc_frame->data[ch] = resampled_buffer[ch];
                                    }
                                    // qDebug()<<"6"<<QTime::currentTime();
                                    avcodec_send_frame(enc_ctx, enc_frame);
                                    while (avcodec_receive_packet(enc_ctx, enc_pkt) >= 0) {
                                        qDebug()<<"useFFmpeg"<<_saveRecord<<sendAudio(udpSocket,enc_pkt->data,enc_pkt->size,filemp3);
                                        av_packet_unref(enc_pkt);
                                    }
                                    for(int ch =0;ch<resampled_frame->ch_layout.nb_channels;ch++){
                                        memmove(resampled_buffer[ch], resampled_buffer[ch] + frame_bytes_per_ch,
                                                buffer_size - frame_bytes_per_ch);
                                    }

                                    buffer_size -= frame_bytes_per_ch;
                                }
                            }
                            av_frame_unref(filtered_frame);
                        }
                        av_frame_unref(dec_frame);
                    }
                }
            }
            av_packet_unref(input_pkt);

        }
    } catch (std::runtime_error err) {
        emit playerStateChanged(PLAYER_ERR,_taskId,_sessionId,err.what());
    }
    if(filemp3!=nullptr){
        fclose(filemp3);
    }
    avformat_close_input(&input_ctx);
    av_dict_free(&options);
    avcodec_free_context(&enc_ctx);
    avcodec_free_context(&dec_ctx);
    avfilter_graph_free(&filter_graph);
    av_frame_free(&dec_frame);
    av_frame_free(&filtered_frame);
    av_frame_free(&resampled_frame);
    av_frame_free(&enc_frame);
    av_packet_free(&input_pkt);
    av_packet_free(&enc_pkt);
    if(resampled_buffer!=nullptr)
        av_freep(resampled_buffer);
    emit playerStateChanged(PLAYER_END,_taskId,_sessionId);
    emit sigRecordFinished();
}

void MediaPlayer::recordAudio()
{
    AVCodecContext *enc_ctx = nullptr;
    AVFrame *enc_frame = av_frame_alloc();
    AVPacket *enc_pkt = av_packet_alloc();
    QUdpSocket udpSocket;

    FILE *filemp3 = nullptr;
    if(_saveRecord){
        fopen_s(&filemp3,QString("record/record_%1_%2.mp3").arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"),_taskId).toStdString().c_str(),"wb");
    }
    try {
        int ret = 0;
        QAudioFormat format;
        format.setSampleFormat(QAudioFormat::Int16);
        format.setSampleRate(44100);
        format.setChannelConfig(QAudioFormat::ChannelConfigStereo);

        // 初始化 MP3 编码器
        const AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_MP3);
        enc_ctx = avcodec_alloc_context3(encoder);
        enc_ctx->bit_rate = _bitrate * 1000;                              // 设置比特率
        enc_ctx->sample_rate = format.sampleRate();             // 采样率
        AVChannelLayout ch_layout;
        av_channel_layout_default(&ch_layout,2);    // 双声道
        av_channel_layout_copy(&enc_ctx->ch_layout,&ch_layout);

        enc_ctx->sample_fmt = AV_SAMPLE_FMT_S16P;           // 设置样本格式
        enc_ctx->time_base = {1, enc_ctx->sample_rate};          // 时间基
        ret = avcodec_open2(enc_ctx, encoder, nullptr);
        if ( ret < 0) {
            throw std::runtime_error(QString("Failed to open encoder : %1").arg(avErr2Str(ret)).toStdString());
        }

        //查找声卡
        QAudioDevice inputDevice;
        bool deviceFound = false;
        for(auto& it:  QMediaDevices::audioInputs()) {
            if(it.description() == _device){
                inputDevice = it;
                deviceFound = true;
            }
        }
        if(!deviceFound){
            inputDevice = QMediaDevices::defaultAudioInput();
        }

        //配置enc_frame
        enc_frame->sample_rate = format.sampleRate();
        enc_frame->format = enc_ctx->sample_fmt;
        av_channel_layout_copy(&enc_frame->ch_layout,&enc_ctx->ch_layout);
        enc_frame->nb_samples = enc_ctx->frame_size;
        av_frame_get_buffer(enc_frame,0);
        //配置音频输入
        int bytes_per_frame = enc_ctx->frame_size * format.bytesPerFrame();

        QByteArray arrayAll;
        QAudioSource audioSrc(inputDevice,format,this);
        audioSrc.setBufferSize(enc_ctx->frame_size * 2);
        auto input = audioSrc.start();
        emit playerStateChanged(PLAYER_START,_taskId,_sessionId);
        while(true){
            QMutexLocker locker(&_mutex);
            if(!_isRunning){
                break;
            }
            if(_isPaused){
                QThread::msleep(5);
                continue;
            }
            locker.unlock();
            auto ba = input->read(bytes_per_frame);
            arrayAll += ba;

            if(arrayAll.size() >= bytes_per_frame){
                ba = arrayAll.left(bytes_per_frame);
                arrayAll.remove(0,bytes_per_frame);
                audioMix(ba,enc_frame,_chMode,enc_ctx->frame_size,format.bytesPerSample());
                avcodec_send_frame(enc_ctx, enc_frame);
                while (avcodec_receive_packet(enc_ctx, enc_pkt) >= 0) {
                    qDebug()<<"NoFFmpeg"<<_saveRecord<<sendAudio(udpSocket,enc_pkt->data,enc_pkt->size,filemp3);
                    av_packet_unref(enc_pkt);
                }
            }
            QThread::msleep(5);
        }


    } catch (std::runtime_error err) {
        emit playerStateChanged(PLAYER_ERR,_taskId,_sessionId,err.what());
    }
    if(filemp3!=nullptr){
        fclose(filemp3);
    }
    avcodec_free_context(&enc_ctx);
    av_packet_free(&enc_pkt);
    emit playerStateChanged(PLAYER_END,_taskId,_sessionId);
    emit sigRecordFinished();
}

void MediaPlayer::start()
{
    qDebug()<<"start:"<<QThread::currentThreadId();
    QMutexLocker locker(&_mutex);
    if(_isRunning && _isPaused){
        emit playerStateChanged(PLAYER_START,_taskId,_sessionId);
    }
    _isRunning = true;
    _isPaused = false;
    locker.unlock();
    _condition.wakeAll(); // 如果线程暂停，唤醒它
    if(!_workThread->isRunning()){
        _workThread->start();
    }
}

void MediaPlayer::stop()
{
    qDebug()<<"stop:"<<QThread::currentThreadId();
    QMutexLocker locker(&_mutex);
    _isRunning = false;
    locker.unlock();
    _condition.wakeAll(); // 唤醒线程以便退出
}

void MediaPlayer::pause()
{
    qDebug()<<"pause:"<<QThread::currentThreadId();
    QMutexLocker locker(&_mutex);
    if(!_isPaused){
        emit playerStateChanged(PLAYER_PAUSE,_taskId,_sessionId);
        _isPaused = true;
    }
}

QString MediaPlayer::avErr2Str(int error)
{
    char error_msg[256];
    av_strerror(error, error_msg, sizeof(error_msg));
    return error_msg;
}

void MediaPlayer::writeFile(AVFrame *frame, FILE *file)
{
    qDebug()<<"writeFile:"<<frame->nb_samples;
    int data_size = av_get_bytes_per_sample((AVSampleFormat)frame->format);
    if (av_sample_fmt_is_planar((AVSampleFormat)frame->format)) {
        // 平面格式处理
        for (int i = 0; i < frame->nb_samples; i++) {
            for (int ch = 0; ch < frame->ch_layout.nb_channels; ch++) {
                fwrite(frame->data[ch] + i * data_size, data_size, 1 , file);
            }
        }
    } else {
        // 非平面格式处理
        fwrite(frame->data[0], 1, frame->nb_samples*data_size*frame->ch_layout.nb_channels, file);
    }
}

int MediaPlayer::sendAudio(QUdpSocket &udpSocket, const uint8_t *data, size_t size, FILE *file)
{
    QByteArray outBA;
    QDataStream out(&outBA, QIODevice::WriteOnly);
    out << _sessionId;
    out << _frameIndex++;
    out << static_cast<int>(0x6D703320);//"mp3 "
    out.writeBytes((const char*)data,size);
    UdpMsg udpMsg;
    udpMsg._inc = _sendInc++;
    udpMsg._cmd = UdpMsg::CMD_RECORDER_SEND;
    udpMsg._subCmd = UdpMsg::SUBCMD_SENDER;
    udpMsg._data = outBA;   
    auto ret = udpSocket.writeDatagram(udpMsg.ToBA(),_audioAddress,_audioPort);
    if(file != nullptr){
        fwrite(outBA.right(size).constData(),1,size,file);
    }
    return ret;
}

void MediaPlayer::audioMix(QByteArray &ba, AVFrame *frame, CH_MODE chMode, int frame_size, int bytes_per_sample)
{
    QDataStream in(ba);
    in.setByteOrder(QDataStream::LittleEndian);

    for(int i =0;i<frame_size;i++){
        short pcm_l,pcm_r,pcm;
        in >> pcm_l>> pcm_r;
        switch (chMode) {
        case CH_STEREO:
            pcm = pcm_l/2+pcm_r/2;
            break;
        case CH_LEFT:
            pcm = pcm_l;
            break;
        case CH_RIGHT:
            pcm = pcm_r;
            break;
        default:
            pcm = pcm_l/2+pcm_r/2;
            break;
        }
        for(int y = 0;y<frame->ch_layout.nb_channels;y++){
            memcpy(frame->data[y]+i*bytes_per_sample, &pcm, sizeof(pcm));
        }
    }
}

QString MediaPlayer::getChModeStr(CH_MODE chMode)
{
    QString ret;
    switch (chMode) {
    case CH_STEREO:
        ret = "stereo";
        break;
    case CH_LEFT:
        ret = "left";
        break;
    case CH_RIGHT:
        ret = "right";
        break;
    default:
        ret = "unknown";
        break;
    }
    return ret;
}
